/**
 * 模拟 keep-alive：用法 <keep-alive> <component></component> </keep-alive>
 * 1.keep-alive 实际也是一个 vue 组件
 * 2.属性包括 include -> 名称匹配的组件就会被缓存 exclude -> 名称匹配的组件不会被缓存 max -> 最多可以缓存多少组件实例
 * 3.keep-alive 包裹动态组件时，会缓存不活动的组件实例
 * 4.实现步骤：1) 判断组件的 name ，不存在 include 或者 存在 exclude 中时，就不缓存该组件，直接返回 vnode
 * 2) 获取组件实例的 key，如果有直接获取，没有重新生成，并且维护一个 keys 数组存储组件实例的 key
 * 3) key 生成规则：cid + '::' + tag，只靠cid 不够，因为相同构造函数可以注册为不同的本地组件
 * 4) 如果缓存对象内存在，则直接从缓存对象中获取组件实例给 vnode，不存在则添加到缓存对象中
 * 5) 缓存组件数量超过 max 时，清除 keys 中第一个 key 对应组件
 * 6) keep-alive 具体是通过 cache 数组缓存所有组件的 vnode 实例。当 cache 内原有组件被使用时会将该组件 key 从 keys 数
 * 组中删除，然后 push 到 keys数组最后，以便清除最不常用组件。
 */

const patternTypes = [String, RegExp, Array]

export default {
  name: 'keep-alive',
  // 表示当前组件是一个抽象组件，不会渲染成一个 DOM 元素，也不会出现在父组件链中
  abstract: true,
  // 父组件传递的参数
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number],
  },
  created() {
    // 初始化虚拟缓存 DOM 数组和 vnode 的 key
    this.cache = Object.create(null)
    this.keys = []
  },
  distroyed() {
    // 组件销毁时清除 cache 的组件实例
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },
  mounted() {
    // 监听 include 和 exclude 值改变，根据最新的值来实时变更缓存的组件内容
    this.$watch('include', (val) => {
      pruneCache(this, (name) => matches(val, name))
    })
    this.$watch('exclude', (val) => {
      pruneCache(this, (name) => !matches(val, name))
    })
  }
}